<?
/**
 * Copyright 2007 Melange.
 *
 * This file is part of PHP-HTTP.
 *
 * PHP-HTTP is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * PHP-HTTP is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PHP-HTTP; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @category    Melange
 * @package     php-http
 * @copyright   Copyright (c) 2007 Jeroen Simons. All rights reserved
 * @author      Jeroen Simons <jeroen@melange.nl>
 * @link        http://www.melange.nl/
 *
 */

class Session {


    public function getValue($prefix, $name) {

        if(array_key_exists("__" . $prefix . "__" . $name, $_SESSION)) {
            if(array_key_exists("__autoload__" . $name, $_SESSION)) {
                $autoload = $_SESSION["__autoload__" . $name];
                require_once($autoload . (preg_match("/\.php$/", $autoload) ? "" : ".php"));
            }
            return unserialize($_SESSION["__" . $prefix . "__" . $name]);

        }

        return null;

    }


    private function setValue($prefix, $name, $value, $autoload=null) {

        // Name cannot be null
        if ($name === null)
            throw new Exception
                ("setAttribute: name parameter cannot be null");

        // Null value is the same as removeAttribute()
        if ($value === null) {
            $this->removeAttribute($name);
            return;
        }

        if($autoload !== null)
            $_SESSION["__autoload__" . $name] = $autoload;

        $_SESSION["__" . $prefix . "__" . $name] = serialize($value);

    }


    private function removeValue($prefix, $name) {
        if (isset($_SESSION["__" . $prefix . "__" . $name])) {
            unset($_SESSION["__" . $prefix . "__" . $name]);
            unset($_SESSION["__autoload__" . $name]);
        }
    }


    /**
     * Return the object bound with the specified name in this session, or
     * <code>null</code> if no object is bound with that name.
     *
     * @param name Name of the attribute to be returned
     *
     * @exception IllegalStateException if this method is called on an
     *  invalidated session
     */
    public function getAttribute($name) {
        return $this->getValue("attr", $name);
    }


    /**
     * Bind an object to this session, using the specified name.  If an object
     * of the same name is already bound to this session, the object is
     * replaced.
     * <p>
     * After this method executes, and if the object implements
     * <code>HttpSessionBindingListener</code>, the container calls
     * <code>valueBound()</code> on the object.
     *
     * @param name Name to which the object is bound, cannot be null
     * @param value Object to be bound, cannot be null
     * @param autoload Path to an Object so
     * autoloading will happen properly. $name must be a path to the class
     *
     * @exception IllegalArgumentException if an attempt is made to add a
     *  non-serializable object in an environment marked distributable.
     * @exception IllegalStateException if this method is called on an
     *  invalidated session
     */
    public function setAttribute($name, $value, $autoload=null) {
        return $this->setValue("attr", $name, $value, $autoload);
    }


    /**
     *
     * Returns an <code>Enumeration</code> of <code>String</code> objects
     * containing the names of all the objects bound to this session.
     *
     * @return			an <code>Enumeration</code> of
     *				<code>String</code> objects specifying the
     *				names of all the objects bound to
     *				this session
     *
     * @exception IllegalStateException	if this method is called on an
     *					invalidated session
     *
     */
    public function getAttributeNames() {

        $names = array();
        $keys  = array_keys($_SESSION);
        foreach($keys as $key)
            if(strpos($key, "__attr__") === 0)
                $names[] = substr($key, 8); 
        return $names;

    }


    /**
     *
     * Removes the object bound with the specified name from
     * this session. If the session does not have an object
     * bound with the specified name, this method does nothing.
     *
     * <p>After this method executes, and if the object
     * implements <code>HttpSessionBindingListener</code>,
     * the container calls
     * <code>HttpSessionBindingListener.valueUnbound</code>. The container
     * then notifies any <code>HttpSessionAttributeListener</code>s in the web
     * application.
     *
     *
     *
     * @param name				the name of the object to
     *						remove from this session
     *
     * @exception IllegalStateException	if this method is called on an
     *					invalidated session
     */
    public function removeAttribute($name) {
        $this->removeValue("attr", $name);
    }


    /**
     * Return the authentication type used to authenticate our cached
     * Principal, if any.
     */
    public function getAuthType() {
        return array_key_exists("__auth_type__", $_SESSION) ?
            $_SESSION["__auth_type__"] : null;
    }


    /**
     * Set the authentication type used to authenticate our cached
     * Principal, if any.
     *
     * @param authType The new cached authentication type
     */
    public function setAuthType($authType) {
        $_SESSION["__auth_type__"] = $authType;
    }


    /**
     * Return the session identifier for this session.
     */
    public function getId() {
        return session_id();
    }


    /**
     * Return the authenticated Principal that is associated with this Session.
     * This provides an <code>Authenticator</code> with a means to cache a
     * previously authenticated Principal, and avoid potentially expensive
     * <code>Realm.authenticate()</code> calls on every request.  If there
     * is no current associated Principal, return <code>null</code>.
     */
    public function getPrincipal() {
        return array_key_exists("__principal__", $_SESSION) ?
            unserialize($_SESSION["__principal__"]) : null;
    }


    /**
     * Set the authenticated Principal that is associated with this Session.
     * This provides an <code>Authenticator</code> with a means to cache a
     * previously authenticated Principal, and avoid potentially expensive
     * <code>Realm.authenticate()</code> calls on every request.
     *
     * @param principal The new Principal, or <code>null</code> if none
     */
    public function setPrincipal($principal) {
        $_SESSION["__principal__"] = serialize($principal);
    }


    /**
     * Return the object bound with the specified name to the internal notes
     * for this session, or <code>null</code> if no such binding exists.
     *
     * @param name Name of the note to be returned
     */
    public function getNote($name) {

        return $this->getValue("note", $name);

    }


    /**
     * Return an Iterator containing the String names of all notes bindings
     * that exist for this session.
     */
    public function getNoteNames() {

        $names = array();
        $keys  = array_keys($_SESSION);
        foreach($keys as $key)
            if(strpos($key, "__note__") === 0)
                $names[] = substr($key, 8); 
        return $names;

    }


    /**
     * Bind an object to a specified name in the internal notes associated
     * with this session, replacing any existing binding for this name.
     *
     * @param name Name to which the object should be bound
     * @param value Object to be bound to the specified name
     */
    public function setNote($name, $value, $autoload=null) {
        $this->setValue("note", $name, $value, $autoload);
    }


    /**
     * Remove any object bound to the specified name in the internal notes
     * for this session.
     *
     * @param name Name of the note to be removed
     */
    public function removeNote($name) {
        $this->removeValue("note", $name);
    }


    /**
     * Set the <code>isNew</code> flag for this session.
     *
     * @param isNew The new value for the <code>isNew</code> flag
     */
    public function setNew($isNew) {
        $this->isNew = $isNew;
    }


    public function __toString() {

        $sb = "[Session";

        $sb .= "\n\tattributes=(";
        $names = $this->getAttributeNames();
        while(list($key, $val) = each($_SESSION)) {
            $sb .= "\n\t\t".$key."=".$val;
            if($i < count($names) - 1)
                $sb .= ",\n\t\t";
        }
        $sb .= "\n\t)";
        $sb .= ",\n\tauthType=".$this->getAuthType();
        $sb .= ",\n\tid=".$this->getId();
        $sb .= "\n\tnotes=(";
        $notes = $this->getNoteNames();
        for($i=0; $i < count($notes); $i++) {
            try {
                $sb .= "\n\t\t".$notes[$i]."=".(is_object($this->getNote($notes[$i])) ?
                    get_class($this->getNote($notes[$i])) : strval($this->getNote($notes[$i])));
                if($i < count($notes) - 1)
                    $sb .= ",";
            } catch(Exception $e) {
                ;
            }
        }
        $sb .= "\n\t)";
        $sb .= ",\n\tprincipal=".$this->getPrincipal();

        $sb .= "\n]";

        return $sb;

    }
    
}

?>